--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
scripts/ci/tree-manifest.sh c0b0ddffd735a0d007c1be9a5e8563ec42fdafc0 (c0b0ddff) Text, 4.42 KB
T8b949e#!/bin/sh
T8b949e# Generate or verify a byte-level SHA-256 inventory of git-tracked files.
T8b949e#
T8b949e# Format (sha256sum style, deterministic):
T8b949e# # meshchatx tree manifest v1
T8b949e# <sha256-hex> <path>
T8b949e#
T8b949e# meshchatx.rsm is excluded from the inventory (avoids a self-hash cycle).
T8b949e# Paths under any vendor/ directory are excluded (vendored trees are not
T8b949e# first-party inventory).
T8b949e#
T8b949e# Paths are listed via newline-delimited git ls-files (POSIX sh / dash safe).
T8b949e# Do not use read -d or sort -z (bash/GNU-only).
T8b949e#
T8b949e# Usage:
T8b949e# tree-manifest.sh generate write inventory to stdout
T8b949e# tree-manifest.sh verify [inventory] verify against file or stdin
T8b949e# tree-manifest.sh verify-tracked [inv] also fail if tracked files are missing from inv
Tffa657set -eu
Te6edf3ROOTTff7b72=Ta5d6ff"Tff7b72$(Te6edf3CDPATHTff7b72= Tffa657cd -- Ta5d6ff"Tff7b72$(dirname Ta5d6ff"Te6edf3$0Ta5d6ff"Tff7b72)Ta5d6ff/../..Ta5d6ff" Tff7b72&& Tffa657pwdTff7b72)Ta5d6ff"
Tffa657cd Ta5d6ff"Te6edf3$ROOTTa5d6ff"
Te6edf3MANIFEST_HEADERTff7b72=Ta5d6ff"# meshchatx tree manifest v1"
Te6edf3EXCLUDE_RSMTff7b72=Ta5d6ff"meshchatx.rsm"
T8b949e# True when path is the root RSM or lives under a vendor directory.
is_excluded_pathTff7b72(Tff7b72) Tff7b72{
Te6edf3fTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff"
Tff7b72[ Ta5d6ff"Te6edf3$fTa5d6ff" Tff7b72= Ta5d6ff"Te6edf3$EXCLUDE_RSMTa5d6ff" Tff7b72] Tff7b72&& Tff7b72return T79c0ff0
Tff7b72case Ta5d6ff"Te6edf3$fTa5d6ff" Tff7b72in
vendor Tb4b4b4| vendor/* Tb4b4b4| */vendor Tb4b4b4| */vendor/*Tff7b72)
Tff7b72return T79c0ff0
Tb4b4b4;Tb4b4b4;
Tff7b72esac
Tff7b72return T79c0ff1
Tff7b72}
file_sha256_streamTff7b72(Tff7b72) Tff7b72{
Tff7b72if Tffa657command -v sha256sum >/dev/null T79c0ff2>Tb4b4b4&T79c0ff1Tb4b4b4; Tff7b72then
sha256sum Tb4b4b4| awk Ta5d6ff'{print $1}'
Tff7b72elif Tffa657command -v shasum >/dev/null T79c0ff2>Tb4b4b4&T79c0ff1Tb4b4b4; Tff7b72then
shasum -a T79c0ff256 Tb4b4b4| awk Ta5d6ff'{print $1}'
Tff7b72else
Tffa657echo Ta5d6ff"tree-manifest.sh: need sha256sum or shasum" >Tb4b4b4&T79c0ff2
Tff7b72return T79c0ff1
Tff7b72fi
Tff7b72}
file_sha256Tff7b72(Tff7b72) Tff7b72{
Te6edf3fTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff"
file_sha256_stream <Ta5d6ff"Te6edf3$fTa5d6ff"
Tff7b72}
T8b949e# Hash the index blob (staged or HEAD), not unstaged working-tree dirt.
index_sha256Tff7b72(Tff7b72) Tff7b72{
Te6edf3fTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff"
git show Ta5d6ff"Ta5d6ff:Te6edf3$fTa5d6ff" Tb4b4b4| file_sha256_stream
Tff7b72}
tracked_pathsTff7b72(Tff7b72) Tff7b72{
git ls-files Tb4b4b4| Te6edf3LC_ALLTff7b72=C sort
Tff7b72}
generateTff7b72(Tff7b72) Tff7b72{
Tffa657printf Ta5d6ff'%s\n' Ta5d6ff"Te6edf3$MANIFEST_HEADERTa5d6ff"
tracked_paths Tb4b4b4| Tff7b72while Te6edf3IFSTff7b72= Tffa657read -r fTb4b4b4; Tff7b72do
Tff7b72[ -n Ta5d6ff"Te6edf3$fTa5d6ff" Tff7b72] Tff7b72|| Tff7b72continue
Tff7b72if is_excluded_path Ta5d6ff"Te6edf3$fTa5d6ff"Tb4b4b4; Tff7b72then
Tff7b72continue
Tff7b72fi
Tff7b72if ! git cat-file -e Ta5d6ff"Ta5d6ff:Te6edf3$fTa5d6ff" T79c0ff2>/dev/nullTb4b4b4; Tff7b72then
Tff7b72continue
Tff7b72fi
Te6edf3modeTff7b72=Ta5d6ff"Tff7b72$(git ls-files -s -- Ta5d6ff"Te6edf3$fTa5d6ff" Tb4b4b4| awk Ta5d6ff'{print $1}'Tff7b72)Ta5d6ff"
Tff7b72case Ta5d6ff"Te6edf3$modeTa5d6ff" Tff7b72in
T79c0ff100644 Tb4b4b4| T79c0ff100755Tff7b72) Tb4b4b4;Tb4b4b4;
*Tff7b72) Tff7b72continue Tb4b4b4;Tb4b4b4;
Tff7b72esac
Te6edf3sumTff7b72=Ta5d6ff"Tff7b72$(index_sha256 Ta5d6ff"Te6edf3$fTa5d6ff"Tff7b72)Ta5d6ff"
Tffa657printf Ta5d6ff'%s %s\n' Ta5d6ff"Te6edf3$sumTa5d6ff" Ta5d6ff"Te6edf3$fTa5d6ff"
Tff7b72done
Tff7b72}
load_inventoryTff7b72(Tff7b72) Tff7b72{
Te6edf3inv_fileTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff"
Tff7b72if Tff7b72[ Ta5d6ff"Te6edf3$inv_fileTa5d6ff" Tff7b72= Ta5d6ff"-" Tff7b72] Tff7b72|| Tff7b72[ -z Ta5d6ff"Te6edf3$inv_fileTa5d6ff" Tff7b72]Tb4b4b4; Tff7b72then
cat
Tff7b72else
cat -- Ta5d6ff"Te6edf3$inv_fileTa5d6ff"
Tff7b72fi
Tff7b72}
verifyTff7b72(Tff7b72) Tff7b72{
Te6edf3check_trackedTff7b72=Ta5d6ff"Tffd700${Te6edf31Tff7b72:-Te6edf30Tffd700}Ta5d6ff"
Te6edf3inv_srcTff7b72=Ta5d6ff"Tffd700${Te6edf32Tff7b72:-Tffd700}Ta5d6ff"
Te6edf3tmpTff7b72=Ta5d6ff"Tff7b72$(mktemp Ta5d6ff"Tffd700${Te6edf3TMPDIRTff7b72:-Tb4b4b4/tmpTffd700}Ta5d6ff/tree-manifest.XXXXXXTa5d6ff"Tff7b72)Ta5d6ff"
Tffa657trap Ta5d6ff'rm -f "$tmp" "$tmp.expect" "$tmp.actual" "$tmp.tracked"' EXIT INT
load_inventory Ta5d6ff"Te6edf3$inv_srcTa5d6ff" >Ta5d6ff"Te6edf3$tmpTa5d6ff"
Te6edf3headerTff7b72=Ta5d6ff"Tff7b72$(sed -n Ta5d6ff'1p' Ta5d6ff"Te6edf3$tmpTa5d6ff"Tff7b72)Ta5d6ff"
Tff7b72if Tff7b72[ Ta5d6ff"Te6edf3$headerTa5d6ff" !Tff7b72= Ta5d6ff"Te6edf3$MANIFEST_HEADERTa5d6ff" Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"Ta5d6fftree-manifest.sh: bad header: Te6edf3$headerTa5d6ff" >Tb4b4b4&T79c0ff2
Tff7b72return T79c0ff1
Tff7b72fi
Te6edf3tmp_expectTff7b72=Ta5d6ff"Tffd700${Te6edf3tmpTffd700}Ta5d6ff.expectTa5d6ff"
Te6edf3tmp_actualTff7b72=Ta5d6ff"Tffd700${Te6edf3tmpTffd700}Ta5d6ff.actualTa5d6ff"
T8b949e# Drop header and blank lines. normalize to "hash path"
sed Ta5d6ff'1d;/^$/d;/^#/d' Ta5d6ff"Te6edf3$tmpTa5d6ff" Tb4b4b4| awk Ta5d6ff'NF>=2 {print $1 " " substr($0, index($0,$2))}' Tb4b4b4| Te6edf3LC_ALLTff7b72=C sort -k2 >Ta5d6ff"Te6edf3$tmp_expectTa5d6ff"
Te6edf3failTff7b72=T79c0ff0
Tff7b72while Te6edf3IFSTff7b72= Tffa657read -r lineTb4b4b4; Tff7b72do
Tff7b72[ -z Ta5d6ff"Te6edf3$lineTa5d6ff" Tff7b72] Tff7b72&& Tff7b72continue
Te6edf3hashTff7b72=Ta5d6ff"Tffd700${Te6edf3lineTb4b4b4%% *Tffd700}Ta5d6ff"
Te6edf3pathTff7b72=Ta5d6ff"Tffd700${Te6edf3lineTb4b4b4#* Tffd700}Ta5d6ff"
Tff7b72if Tff7b72[ ! -f Ta5d6ff"Te6edf3$pathTa5d6ff" Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"Ta5d6fftree-manifest.sh: missing: Te6edf3$pathTa5d6ff" >Tb4b4b4&T79c0ff2
Te6edf3failTff7b72=T79c0ff1
Tff7b72continue
Tff7b72fi
Te6edf3gotTff7b72=Ta5d6ff"Tff7b72$(file_sha256 Ta5d6ff"Te6edf3$pathTa5d6ff"Tff7b72)Ta5d6ff"
Tff7b72if Tff7b72[ Ta5d6ff"Te6edf3$gotTa5d6ff" !Tff7b72= Ta5d6ff"Te6edf3$hashTa5d6ff" Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"Ta5d6fftree-manifest.sh: modified: Te6edf3$pathTa5d6ff" >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff"Ta5d6ff expected Te6edf3$hashTa5d6ff" >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff"Ta5d6ff got Te6edf3$gotTa5d6ff" >Tb4b4b4&T79c0ff2
Te6edf3failTff7b72=T79c0ff1
Tff7b72fi
Tff7b72done <Ta5d6ff"Te6edf3$tmp_expectTa5d6ff"
Tff7b72if Tff7b72[ Ta5d6ff"Te6edf3$check_trackedTa5d6ff" Tff7b72= Ta5d6ff"1" Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3tmp_trackedTff7b72=Ta5d6ff"Tffd700${Te6edf3tmpTffd700}Ta5d6ff.trackedTa5d6ff"
: >Ta5d6ff"Te6edf3$tmp_trackedTa5d6ff"
tracked_paths Tb4b4b4| Tff7b72while Te6edf3IFSTff7b72= Tffa657read -r fTb4b4b4; Tff7b72do
Tff7b72[ -n Ta5d6ff"Te6edf3$fTa5d6ff" Tff7b72] Tff7b72|| Tff7b72continue
Tff7b72if is_excluded_path Ta5d6ff"Te6edf3$fTa5d6ff"Tb4b4b4; Tff7b72then
Tff7b72continue
Tff7b72fi
Tff7b72[ -f Ta5d6ff"Te6edf3$fTa5d6ff" Tff7b72] Tff7b72|| Tff7b72continue
Tff7b72[ -L Ta5d6ff"Te6edf3$fTa5d6ff" Tff7b72] Tff7b72&& Tff7b72continue
Tffa657printf Ta5d6ff'%s\n' Ta5d6ff"Te6edf3$fTa5d6ff"
Tff7b72done Tb4b4b4| Te6edf3LC_ALLTff7b72=C sort >Ta5d6ff"Te6edf3$tmp_trackedTa5d6ff"
awk Ta5d6ff'{print substr($0, index($0,$2))}' Ta5d6ff"Te6edf3$tmp_expectTa5d6ff" Tb4b4b4| Te6edf3LC_ALLTff7b72=C sort >Ta5d6ff"Te6edf3$tmp_actualTa5d6ff"
Te6edf3extraTff7b72=Ta5d6ff"Tff7b72$(comm -13 Ta5d6ff"Te6edf3$tmp_trackedTa5d6ff" Ta5d6ff"Te6edf3$tmp_actualTa5d6ff" Tff7b72|| Tffa657trueTff7b72)Ta5d6ff"
Te6edf3missingTff7b72=Ta5d6ff"Tff7b72$(comm -23 Ta5d6ff"Te6edf3$tmp_trackedTa5d6ff" Ta5d6ff"Te6edf3$tmp_actualTa5d6ff" Tff7b72|| Tffa657trueTff7b72)Ta5d6ff"
Tff7b72if Tff7b72[ -n Ta5d6ff"Te6edf3$extraTa5d6ff" Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"tree-manifest.sh: inventory has paths not tracked (or excluded):" >Tb4b4b4&T79c0ff2
Tffa657printf Ta5d6ff'%s\n' Ta5d6ff"Te6edf3$extraTa5d6ff" >Tb4b4b4&T79c0ff2
Te6edf3failTff7b72=T79c0ff1
Tff7b72fi
Tff7b72if Tff7b72[ -n Ta5d6ff"Te6edf3$missingTa5d6ff" Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"tree-manifest.sh: tracked files missing from inventory (added?):" >Tb4b4b4&T79c0ff2
Tffa657printf Ta5d6ff'%s\n' Ta5d6ff"Te6edf3$missingTa5d6ff" >Tb4b4b4&T79c0ff2
Te6edf3failTff7b72=T79c0ff1
Tff7b72fi
Tff7b72fi
Tff7b72if Tff7b72[ Ta5d6ff"Te6edf3$failTa5d6ff" -ne T79c0ff0 Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"tree-manifest.sh: verification failed" >Tb4b4b4&T79c0ff2
Tff7b72return T79c0ff1
Tff7b72fi
Tffa657echo Ta5d6ff"Ta5d6fftree-manifest.sh: OK (Tff7b72$(wc -l <Ta5d6ff"Te6edf3$tmp_expectTa5d6ff" Tb4b4b4| tr -d Ta5d6ff' 'Tff7b72)Ta5d6ff files)Ta5d6ff"
Tff7b72}
Te6edf3cmdTff7b72=Ta5d6ff"Tffd700${Te6edf31Tff7b72:-Tffd700}Ta5d6ff"
Tff7b72case Ta5d6ff"Te6edf3$cmdTa5d6ff" Tff7b72in
generateTff7b72)
generate
Tb4b4b4;Tb4b4b4;
verifyTff7b72)
verify T79c0ff0 Ta5d6ff"Tffd700${Te6edf32Tff7b72:-Tffd700}Ta5d6ff"
Tb4b4b4;Tb4b4b4;
verify-trackedTff7b72)
verify T79c0ff1 Ta5d6ff"Tffd700${Te6edf32Tff7b72:-Tffd700}Ta5d6ff"
Tb4b4b4;Tb4b4b4;
*Tff7b72)
Tffa657echo Ta5d6ff"Ta5d6ffUsage: Te6edf3$0Ta5d6ff generate | verify [file|-] | verify-tracked [file|-]Ta5d6ff" >Tb4b4b4&T79c0ff2
Tffa657exit T79c0ff2
Tb4b4b4;Tb4b4b4;
Tff7b72esac
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────